﻿using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;

namespace KinonekoSoftware.Extensions.Collections
{
    /// <summary>
    /// 可观测队列。
    /// </summary>
    /// <typeparam name="T">队列元素的类型</typeparam>
    [DebuggerDisplay("Count = {_count}, Head = {_head}, Tail = {_tail}, ver:{_version}")]
    public class ObservableQueue<T> : IEnumerable<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        private int      _head;
        private int      _tail;
        private int      _capacity;
        private int      _count;
        private T[]      _array;
        private int      _version;
        private SpinLock _lock;

        public struct Enumerator : IEnumerator<T>
        {

            private          T                  _current;
            private          int                _version;
            private          int                _index;
            private readonly ObservableQueue<T> _source;

            public Enumerator(ObservableQueue<T> source)
            {
                _current = default(T);
                _source  = source;
                _version = source._version;
                _index   = 0;
            }

            public bool MoveNext()
            {
                if (_version != _source._version)
                {
                    throw new InvalidOperationException("Collection is changed, can't Iterate");
                }
                
                if (_source._count <= 0 || _source._count <= _index)
                {
                    return false;
                }

                if (_index == 0)
                {
                    _current = _source._array[_source._head + _index];
                    _index++;
                    return true;
                }


                if (_source._head < _source._tail)
                {
                    _current = _source._array[_source._head + _index];
                    _index++;
                    return true;
                }
                
                if (_index < _source._tail)
                {
                    _current = _source._array[_index];
                }
                else
                {
                    _index = (_source._head + _index) % _source._count;
                }
                
                _index++;
                return true;
            }

            public void Reset()
            {
                _index   = -1;
                _version = _source._version;
            }

            public T Current
            {
                get
                {
                    if (_index < 0)
                    {
                        throw new InvalidOperationException($"无法获取元素, 因为集合为空, items: {_source._count}");
                    }

                    return _current;
                }
            }

            object IEnumerator.Current => Current;

            public void Dispose()
            {
            }
        }
        
        
        public ObservableQueue() : this(4){}

        public ObservableQueue(int capacity)
        {
            //
            //
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(capacity));
            }

            if (capacity == 0)
            {
                capacity = 4;
            }

            _lock     = new SpinLock();
            _array    = new T[capacity];
            _capacity = capacity;
            _head     = 0;
            _count    = 0;
            _tail     = 0;
            _version  = 0;
        }

        private void Grow()
        {
            if (_capacity > _count)
            {
                return;
            }

            if (_capacity == int.MaxValue)
            {
                throw new ArgumentOutOfRangeException($"{_capacity} large then int.MaxValue");
            }

            //
            //
            _capacity *= 2;

            //
            //
            SetCapacity(_capacity);

        }

        private void SetCapacity(int value)
        {

            //
            //
            var oldArray = _array;
            var newArray = new T[value];

            if (_head < _tail)
            {
                //
                //
                Array.Copy(oldArray, newArray, _count);
            }
            else
            {
                //
                // 队列假溢出，第一段是 [head, count) ,第二段是[0, head)
                Array.Copy(oldArray, _head, newArray, 0, _count - _head);

                Array.Copy(oldArray, 0, newArray, _count - _head, _tail);
            }

            _array = newArray;
            _head  = 0;
            _tail  = _count == value ? 0 : _count;
            ++_version;
        }

        private void UnsafeEnqueue(T item)
        {
            //
            // 确保当前的队列始终大小够用。
            Grow();

            //
            //
            _array[_tail] = item;


            //
            //
            MovePointer(ref _tail);

            //
            //
            _version++;
            _count++;
        }

        private void MovePointer(ref int value)
        {
            var n = value + 1;

            if (n >= _array.Length)
            {
                n = 0;
            }

            value = n;
        }

        public void Enqueue(T item)
        {
            if (!CanAddItem(item))
            {
                return;
            }

            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                //
                // 线程不安全的入队方法
                UnsafeEnqueue(item);
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }
            }
        }

        public T Dequeue()
        {
            if (_count <= 0)
            {
                throw new InvalidOperationException($"{_count}为0，不能出队");
            }

            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                //
                // 线程不安全的出队方法
                var item = _array[_head];

                //
                // 设置为default
                _array[_head] = default(T);


                //
                // 移动指针
                MovePointer(ref _head);

                _version++;
                _count--;

                return item;
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }

            return default(T);
        }

        public T Peek()
        {
            if (_count <= 0)
            {
                throw new InvalidOperationException($"{_count}为0，不能出队");
            }

            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                //
                // 线程不安全的出队方法
                var item = _array[_head];

                return item;
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }

            return default(T);
        }

        public void Clear()
        {
            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                if (_count > 0)
                {
                    if (_head < _tail)
                    {
                        Array.Clear(_array, _head, _count);
                    }
                    else
                    {
                        Array.Clear(_array, _head, _array.Length - _head);
                        Array.Clear(_array, 0, _tail);
                    }
                    
                    _count = 0;
                }

                _head  = 0;
                _tail  = 0;
                _version++;
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }
        }
        
        public bool Contains(T item)
        {
            if (_count <= 0)
            {
                return false;
            }

            if (_head < _tail)
            {
                return Array.IndexOf<T>(_array, item, _head, _count) > -1;
            }


            return Array.IndexOf<T>(_array, item, 0, _tail)              > -1 ||
                   Array.IndexOf<T>(_array, item, _head, _count - _head) > -1;
        }

        public int Count => _count;

        public T[] ToArray()
        {

            if (_count == 0)
            {
                return Array.Empty<T>();
            }


            var newArray = new T[_count];

            if (_head < _tail)
            {
                Array.Copy(_array, _head, newArray, 0, _count);
            }


            Array.Copy(_array, _head, newArray, 0, _count - _head);
            Array.Copy(_array, _head, newArray, _head, _tail);

            return newArray;
        }

        public void CopyTo(T[] newArray)
        {
            if (newArray.Length < _array.Length)
            {
                throw new ArgumentOutOfRangeException($"目标数组的大小小于当前{_array}的大小");
            }

            if (_count == 0)
            {
                return;
            }

            if (_head < _tail)
            {
                Array.Copy(_array, _head, newArray, 0, _count);
            }


            Array.Copy(_array, _head, newArray, 0, _count - _head);
            Array.Copy(_array, _head, newArray, _head, _tail);
        }
        
        public void CopyTo(T[] newArray, int arrayIndex)
        {
            if (newArray.Length  < _array.Length + arrayIndex)
            {
                throw new ArgumentOutOfRangeException($"目标数组的大小小于当前{_array}的大小");
            }

            if (_count == 0)
            {
                return;
            }

            if (_head < _tail)
            {
                Array.Copy(_array, _head, newArray, arrayIndex, _count);
            }


            Array.Copy(_array, _head, newArray, arrayIndex, _count - _head);
            Array.Copy(_array, _head, newArray, arrayIndex         + _head, _tail);
        }

        public bool TryDequeue(out T item)
        {
            if (_count <= 0)
            {
                item = default(T);
                return false;
            }

            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                //
                // 线程不安全的出队方法
                item = _array[_head];

                //
                // 设置为default
                _array[_head] = default(T);


                //
                // 移动指针
                MovePointer(ref _head);

                _version++;
                _count--;

                return true;
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }

            item = default(T);
            return false;
        }

        public bool TryPeek(out T item)
        {
            if (_count <= 0)
            {
                item = default(T);
                return false;
            }

            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);

                //
                // 线程不安全的出队方法
                item = _array[_head];

                return true;
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }

            item = default(T);
            return false;
        }
        
        public void Observe()
        {
            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);
                
                //
                //
                PropertyChanged?.Invoke(this, CollectionStatic.CountPropertyChanged);
                PropertyChanged?.Invoke(this, CollectionStatic.IndexerPropertyChanged);

                //
                //
                CollectionChanged?.Invoke(this, CollectionStatic.ResetCollectionChanged);
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }
        }

        public void Observe(SynchronizationContext context)
        {
            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);
                
                context?.Send(_ =>
                {
                    //
                    //
                    PropertyChanged?.Invoke(this, CollectionStatic.CountPropertyChanged);
                    PropertyChanged?.Invoke(this, CollectionStatic.IndexerPropertyChanged);

                    //
                    //
                    CollectionChanged?.Invoke(this, CollectionStatic.ResetCollectionChanged);
                }, null);
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }
        }

        public void Observe(Action<Action> synchronization)
        {
            //
            // 线程安全的实现方法
            var lockTaken = false;

            try
            {
                _lock.Enter(ref lockTaken);
                
                synchronization?.Invoke(() =>
                {
                    //
                    //
                    PropertyChanged?.Invoke(this, CollectionStatic.CountPropertyChanged);
                    PropertyChanged?.Invoke(this, CollectionStatic.IndexerPropertyChanged);

                    //
                    //
                    CollectionChanged?.Invoke(this, CollectionStatic.ResetCollectionChanged);
                });
            }
            catch(Exception ex)
            {
                //
                //
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                if (lockTaken)
                {
                    _lock.Exit(false);
                }

            }
        }
        
        public IEnumerator<T> GetEnumerator() => new Enumerator(this);

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);

        /// <summary>
        /// 重写该方法，实现添加前检测
        /// </summary>
        /// <param name="item">要添加的项目。</param>
        /// <returns>如果可以添加，则返回true，否则返回false。</returns>
        protected virtual bool CanAddItem(T item) => true;
        
        
        public event PropertyChangedEventHandler         PropertyChanged;
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    }
}